/*
 * Copyright the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package asphalt2.factory.impl
{
	import asphalt2.factory.BeanFactory;
	import asphalt2.factory.impl.bind.LinkedBindingBuilder;
	import asphalt2.factory.init;
	
	import flash.events.Event;
	import flash.events.IEventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.utils.describeType;
	import flash.utils.getQualifiedClassName;
	
	import mx.collections.ArrayCollection;
	import mx.events.StyleEvent;
	import mx.logging.ILogger;
	import mx.logging.Log;
	import mx.styles.CSSStyleDeclaration;
	import mx.styles.StyleManager;
	
	use namespace init;
	use namespace asphalt2container_internal;
	
	[Event(name="progress",type="flash.events.ProgressEvent")]	
	[Event(name="ioError",type="flash.events.IOErrorEvent")]
	[Event(name="complete",type="flash.events.Event")]
	
	/**
	 * CSSで設定を行うBean生成Factory.
	 * @auther c9katayama
	 */
	public class CSSBeanFactory implements BeanFactory,IEventDispatcher
	{		
		private static const BEAN_REF_PATTEN:RegExp = /[$][{][^)]*[}]/;
		private static var logger:ILogger = Log.getLogger(getQualifiedClassName(CSSBeanFactory).replace("::","."));		
		
		public static const TYPE_CONFIG_NAME:String = "beanType";
		public static const SCOPE_CONFIG_NAME:String = "beanScope";
		public static const INIT_METHOD_CONFIG_NAME:String = "initMethod";		
		private static const CONFIG_NAME_LIST:Array = [TYPE_CONFIG_NAME,SCOPE_CONFIG_NAME,INIT_METHOD_CONFIG_NAME];

		public static const METADATA_INJECT:String = "Inject";
		public static const METADATA_INIT:String = "Init";

		public static const SCOPE_SINGLETON:String = "singleton"
		public static const SCOPE_PROTOTYPE:String = "prototype"

		private static var _defaultInstance:CSSBeanFactory;
		
		protected var _configLoader:ModuleConfigLoader;
		protected var _typeInfoCache:TypeInfoCache;
		protected var _binder:Binder;		
		protected var _beanScopeManager:BeanScopeManager;
		
		protected static var _instantiated:Boolean = false;
		protected var _initialized:Boolean = false;
		protected var _defaultBeanScope:String;
		
		public function CSSBeanFactory(){
			if(_instantiated){
				throw new Error("Cannot instantiate.Use getInstance() method.");
			}
			_typeInfoCache = new TypeInfoCache();
			_beanScopeManager = new BeanScopeManager();
			_configLoader = new ModuleConfigLoader();
			_binder = new Binder();
			_instantiated = true;
		}
		/**
		 * デフォルトインスタンスを返します
		 */
		public static function getInstance():CSSBeanFactory{
			if(_defaultInstance == null){
				_defaultInstance = new CSSBeanFactory();
			}
			return _defaultInstance;
		}
		protected function initialize():void{
			if(_defaultBeanScope==null){
				var beanDesc:BeanDesc = findBindValue("CSSBeanFactory");
				var scope:String = SCOPE_SINGLETON;
				if(beanDesc != null){
					var configScope:String = beanDesc.getValue("defaultBeanScope");
					if(configScope != null){
						scope = configScope;
					}
				}
				defaultBeanScope = scope;
			}
			_initialized = true;
		}
		/**
		 * Beanのデフォルトスコープをセットします。
		 */
		public function set defaultBeanScope(scope:String):void{
			checkScopeValue(scope);
			_defaultBeanScope = scope;
			logger.info("defaultBeanScope="+scope);
		}
		/**
		 * Classのショートネームを使用して、beanを検索し、beanインスタンスを返します。
		 */		
		public function getBeanByType(clazz:Class):*{
			var beanName:String = Util.getShortClassName(clazz);
			var bean:Object = getBean(beanName);
			if(bean is clazz){
				return bean;
			}else{
				throw new Error("Type mismatch error.　required type="+clazz+" ,but bean type="+bean);
			}
		}
		/**
		 * beanNameを使用してbeanを検索し、beanインスタンスを返します。
		 */
		public function getBean(beanName:String):*{			
			_beanScopeManager.init();
			return resolveManagedValue(beanName);
		}
		/**
		 * 引数のbeanインスタンスに対して、値を設定します。
		 */		
		public function inject(bean:Object):*{
			_beanScopeManager.init();
			var context:BeanResolveContext = new BeanResolveContext();
			context.beanName = "";
			context.beanDesc = new BindBeanDesc();
			var clazz:Class = Util.getClass(bean);
			context.beanClass = clazz;
			context.bean = bean;
			context.typeInfo = _typeInfoCache.getTypeInfo(context.beanClass);
			context.scope = SCOPE_PROTOTYPE;
			resolve(context);
			return context.bean;	
		}
		
		public function bind(nameOrClass:*):LinkedBindingBuilder{
			var name:String = null;
			if(nameOrClass is Class){
				name = Util.getShortClassName(nameOrClass);
			}else if(nameOrClass is String){
				name = String(nameOrClass);
			}else{
				throw new ArgumentError("Required Class or String argument.");
			}
			name = toInternalName(name);
			return new LinkedBindingBuilder(name,this);
		}
		public function bindConstant(name:String,value:*):void{
			name = toInternalName(name);
			_binder.bind(name,value);
		}
		public function loadModule(url:String):void{
			_configLoader.loadModule(url);
		}
		private function resolveManagedValue(name:String):Object{
			name = toInternalName(name);
			if(_initialized==false){
				initialize();
			}
			var resolvedBean:Object = _beanScopeManager.findBeanByName(name);
			if(resolvedBean != null){
				return resolvedBean;
			}
			
			var bindValue:* = findBindValue(name);
			if(bindValue == null){
				throw new Error("Value not found. name="+name);
			}
			if((bindValue is BeanDesc)==false){
				//定数・インスタンス
				return bindValue;
			}
			var context:BeanResolveContext = new BeanResolveContext();
			context.beanName = name;
			context.beanDesc = BeanDesc(bindValue);
			var clazz:Class = Class(context.beanDesc.getValue(TYPE_CONFIG_NAME));
			if(clazz == null){
				throw new Error("[bean-type] property is not defined. beanName="+context.beanName);
			}
			context.beanClass = clazz;
			context.bean = new clazz();
			context.typeInfo = _typeInfoCache.getTypeInfo(context.beanClass);
			
			resolve(context);
			
			logger.debug("beanName="+context.beanName+
				" class="+getQualifiedClassName(context.beanClass)+ " scope="+context.scope);
			return context.bean;
		}
		private function resolve(context:BeanResolveContext):void{
			initScope(context);
			resolveProperty(context);
			invokeInjectMethod(context);
			invokeInitMethod(context);
		} 
		private function findBindValue(name:String):*{			
			var selectorProperty:Array = name.split(".");
			if(selectorProperty.length==2){/** "jdbc.driver"のようなパターン */
				var selector:String = selectorProperty[0];
				var property:String = selectorProperty[1];
				var constdesc:CSSStyleDeclaration = findStyleDeclaration(selector);
				if(constdesc!=null){
					var value:* = constdesc.getStyle(property);
					if(value != null){
						return evaluate(value);
					}
				}
			}
			var desc:CSSStyleDeclaration = findStyleDeclaration(name);
			if(desc != null){
				return new CSSBeanDesc(name,desc);
			}
			//CSSになければBindしたものを探す
			return evaluate(_binder.getBindValue(name));
		}
		private function findStyleDeclaration(name:String):CSSStyleDeclaration{
			var desc:CSSStyleDeclaration = StyleManager.getStyleDeclaration(name);
			if(desc==null){
				desc = StyleManager.getStyleDeclaration("."+name);
			}
			return desc;
		}
		
		private function initScope(context:BeanResolveContext):void{
			if(context.scope==null){
				var scope:String = context.beanDesc.getValue(SCOPE_CONFIG_NAME);
				if(scope==null){
					scope = _defaultBeanScope;
				}
				checkScopeValue(scope);
				_beanScopeManager.addBean(scope,context.beanName,context.bean);
				context.scope = scope;
			}
		}
		private function resolveProperty(context:BeanResolveContext):void{
			const resolvedProperty:ArrayCollection = new ArrayCollection();			
			const bean:Object = context.bean;
			//bean desc
			var descMap:Object = context.beanDesc.getValues();
			for(var descName:String in descMap){
	        	if(isConfigName(descName)==false){
		         	var value:Object = descMap[descName];
		         	try{
		           		value = evaluate(value);
		          	}catch(e:Error){
						throw ArgumentError("Property not found. name="+context.beanName+" property="
										+descName+" <cause>"+Util.toErrorString(e)+"\n</cause>");
					}
					try{
						bean[descName] = value;
					}catch(e:Error){
						throw ArgumentError("Property set fail. name="+context.beanName+" property="
										+descName+" <cause>"+Util.toErrorString(e)+"\n</cause>");
					}
		           	resolvedProperty.addItem(descName);
		        }
	        }
	        //Inject metadata
			const typeInfo:XML = context.typeInfo;
			const accessorList:XMLList = typeInfo.factory.accessor;
			const variableList:XMLList = typeInfo.factory.variable;
			var propertyList:XMLList = new XMLList(accessorList.toXMLString());
			for each(var variable:XML in variableList){
				propertyList[propertyList.length()] = variable;
			}			
			const injectPropertyList:Array = Util.getInfoListWithMetadata(propertyList,METADATA_INJECT);
			for each(var injectProperty:XML in injectPropertyList){
				if(resolvedProperty.contains(injectProperty.@name)==false){
					var meta:XML =XMLList(injectProperty.metadata.(@name==METADATA_INJECT))[0];
					var nameArg:XMLList = meta.arg.(@key='name');					
					if(nameArg.length() <= 0){
						nameArg = meta.arg.(@key='');
					}
					var propName:String = null;
					if(nameArg.length() > 0){
						propName = nameArg[0].@value;
					}
					if(Util.isEmpty(propName)){
						var propType:String = injectProperty.@type;
						propName = Util.getShortClassName(propType);
					}
					var propertyValue:Object = null;
					try{
						propertyValue = resolveManagedValue(propName);
					}catch(e:Error){
						var required:Boolean = Util.getBoolean(meta.@required,true);
						if(required==true){
							throw ArgumentError("Property not found. name="+context.beanName+" property="
										+injectProperty.@name+" <cause>"+Util.toErrorString(e)+"\n</cause>");
						}
					}
					if(propertyValue != null){
						try{
							bean[injectProperty.@name] = propertyValue;
						}catch(e:Error){
							throw ArgumentError("Property set fail. name="+context.beanName+" property="
											+injectProperty.@name+" <cause>"+Util.toErrorString(e)+"\n</cause>");
						}						
					}
				}
			}
		}
		private function invokeInjectMethod(context:BeanResolveContext):void{
			const bean:Object = context.bean;
			const typeInfo:XML = context.typeInfo;
			const methodList:XMLList = typeInfo.factory.method;			
			const injectMethodList:Array = Util.getInfoListWithMetadata(methodList,METADATA_INJECT);
			for each(var injectMethod:XML in injectMethodList){
				var metaMethodArgs:Array = resolveArguments(injectMethod);
				try{
					bean[injectMethod.@name].apply(bean,metaMethodArgs);
				}catch(e:Error){
					throw new ArgumentError("Invoke fail.name="+context.beanName+" method="
										+injectMethod.@name+" <cause>"+Util.toErrorString(e)+"\n</cause>");
				}
			}
		}
		private function evaluate(value:Object):Object{
			if(value == null){
				return null;
			}
			if(value is String){
				var valueString:String = value as String;
				if(BEAN_REF_PATTEN.test(valueString)){
					valueString = valueString.substring("${".length,valueString.length-1);
					return resolveManagedValue(valueString);
				}
			}else if(value is Array){
				var valueArray:Array = value as Array;
				for(var i:int = 0;i < valueArray.length;i++){
					valueArray[i] = evaluate(valueArray[i]);
				}
				return valueArray;
			}
			return value;
		}
		private function invokeInitMethod(context:BeanResolveContext):void{
			const typeInfo:XML = context.typeInfo;
			const methodInfoList:XMLList = typeInfo.factory.method;
			const bean:Object = context.bean;
			const invokedInitMethodList:ArrayCollection = new ArrayCollection();
			//init namespace
			var initNamespaceMathodList:XMLList = methodInfoList.(attribute('uri') == init.uri);
			for each(var initNamespaceMethod:XML in initNamespaceMathodList){
				var namespaceMethodArgs:Array = resolveArguments(initNamespaceMethod);
				try{
					bean.init::[initNamespaceMethod.@name].apply(bean,namespaceMethodArgs);
				}catch(e:Error){
					throw new ArgumentError("Invoke fail.name="+context.beanName+" method="
										+initNamespaceMethod.@name+" <cause>"+Util.toErrorString(e)+"\n</cause>");					
				}
				invokedInitMethodList.addItem(initNamespaceMethod);
			}
			//css desc
			var initMethodDesc:Object = context.beanDesc.getValue(INIT_METHOD_CONFIG_NAME);
			if(initMethodDesc != null){
				var initMethodNameList:Array = 
					(initMethodDesc is String) ? [initMethodDesc] : initMethodDesc as Array;
				for each(var methodName:String in initMethodNameList){
					var descMethodList:XMLList = methodInfoList.(@name==methodName);
					for each(var descMethod:XML in descMethodList){
						if(invokedInitMethodList.contains(descMethod)==false){
							var methodArgs:Array = resolveArguments(descMethod);
							try{
								bean[descMethod.@name].apply(bean,methodArgs);
							}catch(e:Error){
								throw new ArgumentError("Invoke fail.name="+context.beanName+" method="
													+descMethod.@name+" <cause>"+Util.toErrorString(e)+"\n</cause>");					
							}
							invokedInitMethodList.addItem(descMethod);
						}						
					}
				}
			}
			//Init metadata
			var initMetadataMethodList:Array = Util.getInfoListWithMetadata(methodInfoList,METADATA_INIT);
			for each(var initMetadataMethod:XML in initMetadataMethodList){
				if(invokedInitMethodList.contains(initMetadataMethod)==false){
					var metaMethodArgs:Array = resolveArguments(initMetadataMethod);
					try{
						bean[initMetadataMethod.@name].apply(bean,metaMethodArgs);
					}catch(e:Error){
						throw new ArgumentError("Invoke fail.name="+context.beanName+" method="
											+initMetadataMethod.@name+" <cause>"+Util.toErrorString(e)+"\n</cause>");						
					}
				}
			}
		}
		private function resolveArguments(methodInfo:XML):Array{
			var params:XMLList = methodInfo.parameter;
			var argumentList:Array = [];
			for each(var param:XML in params){
				var paramType:String = param.@type;
				var paramBeanName:String = Util.getShortClassName(paramType);
				try{
					argumentList.push(resolveManagedValue(paramBeanName));
				}catch(e:Error){
					if(param.@optional=="true"){
						argumentList.push(null);
					}else{
						throw ArgumentError("argument not found.argument type="+paramType+" [cause]"+Util.toErrorString(e));
					}
				}
			}
			return argumentList
		}
		private function isConfigName(name:String):Boolean{
			for(var i:int = 0;i < CONFIG_NAME_LIST.length;i++){
				if(name==CONFIG_NAME_LIST[i]){
					return true;
				}				
			}
			return false;
		}		
		private function checkScopeValue(scope:String):void{			
			if(scope != SCOPE_SINGLETON && scope != SCOPE_PROTOTYPE){
				throw new Error("BeanScope ["+scope+"] not support.");
			}			
		}
		
		//IEventDispatcher
		public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void {
			_configLoader.addEventListener(type,listener,useCapture,priority,useWeakReference);
		}
		public function dispatchEvent(event:Event):Boolean{
			return _configLoader.dispatchEvent(event);
		}
		public function hasEventListener(type:String):Boolean{
			return _configLoader.hasEventListener(type);
		}
		public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void{
			_configLoader.removeEventListener(type,listener,useCapture);
		}
		public function willTrigger(type:String):Boolean {
			return _configLoader.willTrigger(type);
		}
		
		asphalt2container_internal function createBeanDesc(name:String,clazz:Class):void{
			var beanDesc:BindBeanDesc = new BindBeanDesc();
			beanDesc.setValue(TYPE_CONFIG_NAME,clazz);
			_binder.bind(name,beanDesc);
		}
		asphalt2container_internal function setBeanDescValue(beanDescKey:String,key:String,value:*):void{
			var beanDesc:BindBeanDesc = _binder.getBindValue(beanDescKey);
			beanDesc.setValue(key,value);			
		}
		asphalt2container_internal function createConstant(key:String,value:*):void{
			_binder.bind(key,value);
		}
		/**
		 * 内部で保持する名前に変換します。
		 */
		private static function toInternalName(name:String):String{
			if(name.indexOf(".")==0){
				name = name.substring(1);
			}
			return Util.toCamelCase(name);
		}					
	}
}

class BeanScopeManager{
	private static var singletonScopeMap:Object = {};
	private var resolveScopeMap:Object;
	
	public function init():void{
		resolveScopeMap = {};
	}
	public function addBean(scope:String,beanName:String,bean:Object):void{
		if(scope == CSSBeanFactory.SCOPE_SINGLETON){
			singletonScopeMap[beanName] = bean;
		}else{
			resolveScopeMap[beanName] = bean;		
		}
	}
	
	public function findBeanByName(beanName:String):Object{
		var resolvedBean:Object = singletonScopeMap[beanName];
		if(resolvedBean == null){
			resolvedBean = resolveScopeMap[beanName];
		}
		return resolvedBean;
	}
}

import mx.styles.CSSStyleDeclaration;
import flash.utils.Dictionary;
import flash.utils.describeType;
import flash.utils.getQualifiedClassName;
import asphalt2.factory.impl.CSSBeanFactory;
import flash.utils.getDefinitionByName;
import flash.events.IEventDispatcher;
import mx.events.StyleEvent;
import mx.styles.StyleManager;
import flash.events.ProgressEvent;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.EventDispatcher;
import flash.system.ApplicationDomain;
import flash.sampler.startSampling;
import mx.modules.ModuleLoader;
import mx.modules.ModuleManager;
import mx.modules.Module;
import mx.modules.IModuleInfo;
import mx.events.ModuleEvent;
import mx.core.IFlexModuleFactory;

class BeanResolveContext{
	public var bean:Object;
	public var beanClass:Class;
	public var beanName:String;
	public var beanDesc:BeanDesc;
	public var resolveScopeMap:Object;
	public var typeInfo:XML;
	public var scope:String;
}

class Binder{
	private var bindMap:Object = {};
	
	public function bind(key:String,value:*):void{
		if(bindMap[key]!=null){
			throw new Error("["+key+"] is already binded.");
		}
		bindMap[key]=value;
	}
	public function getBindValue(key:String):*{
		return bindMap[key];
	}
}

interface BeanDesc{
	function getValue(key:String):*;
	function getValues():Object;
}
class CSSBeanDesc extends CSSStyleDeclaration implements BeanDesc {
	private var selector:String;
	private var desc:CSSStyleDeclaration;
	
	public function CSSBeanDesc(selector:String,desc:CSSStyleDeclaration){
		this.selector = selector;
		this.desc = desc;
	}	
	public function getValue(key:String):*{
		return desc.getStyle(key);
	}
	public function getValues():Object{
		const factory:Function = desc.factory;
		if (factory != null){
            factory.prototype = {};
            var configMap:Object = new factory();
            return configMap;
  		}else{
  			//スクリプト内でstyle定義した場合や、swf化したCSSをロードした場合、CSSStyleDeclarationの
  			//overridesというプロパティにしか値が入らない。このプロパティはprotectedで取れないため、エラーにする
  			throw new Error("If the setStyle() method is called at run time ,or CSS conterd to swf, cannnot get values.");    
  		}
  	}
}
class BindBeanDesc implements BeanDesc{
	private var descMap:Object = {};
	public function getValue(key:String):*{
		return descMap[key];
	}
	public function getValues():Object{
		return descMap;
	}
	public function setValue(key:String,value:*):void{
		descMap[key]=value;
	}
}

class TypeInfoCache{
	private static const cache:Dictionary = new Dictionary(true);
	public function getTypeInfo(clazz:Class):XML{
		var info:XML = cache[clazz];
		if(info == null){
			info = describeType(clazz);
			cache[clazz] = info;
		}
		return info;
	}
}
class ModuleConfigLoader extends EventDispatcher{
	
	private var loadingModuleInfoMap:Object = {};
	
	public function ModuleConfigLoader(){
		
	}
	public function loadModule(url:String):void{
		var moduleInfo:IModuleInfo = ModuleManager.getModule(url);
		if(loadingModuleInfoMap[url] != null){
			return;
		}
		moduleInfo.addEventListener(ModuleEvent.ERROR,handleModuleError);
		moduleInfo.addEventListener(ModuleEvent.PROGRESS,handleModuleProgress);
		moduleInfo.addEventListener(ModuleEvent.READY,handleModuleReady);
		moduleInfo.load(ApplicationDomain.currentDomain);
		loadingModuleInfoMap[url] = moduleInfo;
	}
	private function handleModuleError(e:ModuleEvent):void{
		delete loadingModuleInfoMap[e.module.url];	
		var event:Event = new IOErrorEvent(IOErrorEvent.IO_ERROR,e.bubbles,e.cancelable,e.toString());
		dispatchEvent(event);
	}
	private function handleModuleProgress(e:ModuleEvent):void{
		var event:Event = new ProgressEvent(ProgressEvent.PROGRESS,e.bubbles,e.cancelable,e.bytesLoaded,e.bytesTotal);
		dispatchEvent(event);
	}
	private function handleModuleReady(e:ModuleEvent):void{
		delete loadingModuleInfoMap[e.module.url];
		//インスタンス化
		var moduleFactory:IFlexModuleFactory = e.module.factory; 
		var module:* = Module(moduleFactory.create());
		//CSSのセレクタがかぶっていたりするとインスタンス化に失敗する
		if(module == null){
			var errorMessage:String = "Cannot instaciate CSS Module. url="+e.module.url+ " Check MXML,AS,CSS.";
			throw new Error(errorMessage);
		}
		var event:Event = new Event(Event.COMPLETE,e.bubbles,e.cancelable);
		dispatchEvent(event)		
	}
}
/*
class CSSConfigLoader extends EventDispatcher{
	
	public function CSSConfigLoader(){
	}
	public function loadConfig(url:String):void{
		var ed:IEventDispatcher = StyleManager.loadStyleDeclarations(url,true,false,ApplicationDomain.currentDomain);
		ed.addEventListener(StyleEvent.PROGRESS,handleProgress);			
		ed.addEventListener(StyleEvent.COMPLETE,handleComplate);
		ed.addEventListener(StyleEvent.ERROR,handleError);	
	}
	private function handleProgress(e:StyleEvent):void{
		var event:Event = new ProgressEvent(ProgressEvent.PROGRESS,e.bubbles,e.cancelable,e.bytesLoaded,e.bytesTotal);
		dispatchEvent(event);
	}
	private function handleComplate(e:StyleEvent):void{
		var event:Event = new Event(Event.COMPLETE,e.bubbles,e.cancelable);
		dispatchEvent(event);	
	}
	private function handleError(e:StyleEvent):void{
		//TODO StyleEvent.ERRORが2度来るため、初回のエラーのみ取得。（errorTextで判定）
		if(e.errorText.indexOf("Error")==0){
			var event:Event = new IOErrorEvent(IOErrorEvent.IO_ERROR,e.bubbles,e.cancelable,e.toString());
			dispatchEvent(event);
		}
	}
}
*/
class Util{
	public static function toErrorString(e:Error):String{
		var str:String = e.message;
		if(str == null){
			str = "";
		}else{
			str += "\n";
		}
		str += e.getStackTrace();
		return str;
	}
	public static function isEmpty(value:Object):Boolean{
		return (value == null || value.toString().length==0);
	}
	public static function getBoolean(value:Object,nullValue:Boolean):Boolean{
		if(value!=null){
			var strValue:String = value.toString().toLowerCase();
			if(strValue.length>0){
				if(strValue=="true"||strValue=="yes"){
					return true;
				}
				if(strValue=="false"||strValue=="no"){
					return false;
				}
				throw new Error("Invalid boolean value. value=" + value);
			}
		}
		return nullValue;
	}
    public static function toCamelCase( text:String,delimiter:String = '-'):String{
            var sb:String = '';
            var splitArr:Array = text.split(delimiter);
            for( var i:int = 0; i < splitArr.length; i++ ){
				var s:String = splitArr[i] as String;
				if( i != 0 && s.length >0 ){
					s = s.substring(0,1).toUpperCase()+s.substring(1,s.length);
				}
				sb += s;
            }
            return sb;
    }

	/**
	 * クラスのショートネームを取得します。
	 * @param clazz Class or String
	 * @return クラスショートネーム
	 */
	public static function getShortClassName(clazz:*):String{
		var name:String = (clazz is String ) ? clazz as String : getQualifiedClassName(clazz);
		name = name.replace("::",".");
		var dot:int = name.lastIndexOf(".");
		if(dot!=-1){
			name = name.substring(dot+1,name.length);
		}
		return name;			
	}
	public static function getClass(o:Object):Class{
		var name:String = getQualifiedClassName(o);
		name = name.replace("::",".");
		return Class(getDefinitionByName(name));
	}
	/**
	 * メソッド・プロパティのリストから、指定のメタデータの付与されたものの配列を返します。
	 * @param infoList 
	 * @param metadata 
	 */
	public static function getInfoListWithMetadata(infoList:XMLList,metadata:String):Array{
		var result:Array = [];
		if ( infoList.hasOwnProperty( 'metadata' ) ) {
			for each(var info:XML in infoList){
				var meta:XMLList = info.metadata.(@name==metadata);
				if(meta.length()>0){
					result.push(info);
				}
			}
		}
		return result;
	}
	public static function addOnceEventListener(dispatcher:IEventDispatcher,eventType:String,listener:Function):void{
		var f:Function = function(e:Event):void{
			dispatcher.removeEventListener(eventType,f);
			listener(e);
		};
		dispatcher.addEventListener(eventType,f);
	}	
}

